import os
import random
import re
import subprocess
import threading
import time
import shutil
from enum import Enum
from typing import List,Dict
from datetime import datetime
from concurrent.futures import ThreadPoolExecutor
from openai import OpenAI

VOLCENGINE_API_KEY="af9e451e-86db-4d0c-af8b-2ca50de7a03a"
VOLCENGINE_BASE_URL="https://ark.cn-beijing.volces.com/api/v3"
VOLCENGINE_MODELS=["ep-20250213131249-k9d5m","ep-20250213151523-6zssc","ep-20250214181051-k8djc"]

QWEN_API_KEY="sk-ac2f0bc476f144fcbf76528430bbe2c7"
QWEN_BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1"
QWEN_MODELS=["qwen-max-latest"]

INPUT_BASE_DIR="Input"
INPUT_PROJECT_DIR="02-Medium"
OUTPUT_BASE_DIR="Output"
MAX_LOOP_IN_TRANSLATION_TASK_THREAD=1
MAX_LOOP_IN_THREAD_POOL=4
SINGLE_FILE_MAX_THREAD=10
SINGLE_FILE_THREAD=10
SINGLE_FILE_THREAD_QWEN=2
SINGLE_FILE_THREAD_DEEPSEEK_V3=3
SINGLE_FILE_THREAD_DOUBAO=1

class LLMType(Enum):
    VOLCENGINE="火山引擎"
    QWEN="通义千问"

class LLMInfo:
    def __init__(self,type:LLMType,models:list[str],client:OpenAI):
        self.type=type
        self.models=models
        self.client=client

class InputFileDir(Enum):
    SRC="src"
    TEST="test"

class OutputFileDir(Enum):
    SRC="src"
    TESTS="tests"

class TranslationResult(Enum):
    RESPONSE_EXCEPTION=1
    BUILD_ERROR=2
    TEST_FAILED=3
    SUCCESS=4

class InputFile:
    def __init__(self,file_dir:InputFileDir,file_name:str,file_content:str):
        self.file_dir=file_dir
        self.file_name=file_name
        self.file_content=file_content
        self.includes_in_src:Dict[str,InputFile]={}
        self.be_included_in_src:Dict[str,InputFile]={} 
        self.be_included_in_test:Dict[str,InputFile]={}
        self.output_file:OutputFile=None

    def __str__(self):
        string=f"file_dir={self.file_dir.value},file_name={self.file_name}\n"   
        string+=f"includes_in_src:\n"
        for input_file in self.includes_in_src.values():
            string+=f"{input_file.file_name}\n"
        string+=f"\nbe_included_in_src:\n"   
        for input_file in self.be_included_in_src.values():
            string+=f"{input_file.file_name}\n"
        string+=f"\nbe_included_in_test:\n"     
        for input_file in self.be_included_in_test.values():
            string+=f"{input_file.file_name}\n"
        if self.output_file is not None:    
            string+=f"\noutput_file:{self.output_file.file_name}\n" 
        else:    
            string+=f"\noutput_file:None\n"    
        return string   

class OutputFile:
    def __init__(self,file_dir:OutputFileDir,file_name:str):
        self.file_dir=file_dir
        self.file_name=file_name
        self.need_src_mods:Dict[str,OutputFile]={}
        self.file_content=""  

    def __str__(self):
        return f"file_dir={self.file_dir.value},file_name={self.file_name},\tneed_src_mods={list(self.need_src_mods.keys())}"    

class InputProject:
    def __init__(self,project_path:str):
        self.project_path=project_path
        self.src_h_files:Dict[str,InputFile]={}
        self.src_c_cpp_files:Dict[str,InputFile]={}
        self.test_h_files:Dict[str,InputFile]={}
        self.test_c_cpp_files:Dict[str,InputFile]={}

    def __str__(self):
        string=f"project_path={self.project_path}\n"
        string+=f"src_h_files({len(self.src_h_files)}个):\n"
        string+=f"src_c_cpp_files({len(self.src_c_cpp_files)}个):\n"
        string+=f"test_h_files({len(self.test_h_files)}个):\n"
        string+=f"test_c_cpp_files({len(self.test_c_cpp_files)}个):\n\n"
        string+="详情如下：\n"
        string+=f"src_h_files({len(self.src_h_files)}个):\n"
        for input_file in self.src_h_files.values():
            string+=f"{input_file}\n"
        string+=f"\nsrc_c_cpp_files({len(self.src_c_cpp_files)}个):\n"
        for input_file in self.src_c_cpp_files.values():
            string+=f"{input_file}\n"
        string+=f"\ntest_h_files({len(self.test_h_files)}个):\n"
        for input_file in self.test_h_files.values():
            string+=f"{input_file}\n"
        string+=f"\ntest_c_cpp_files({len(self.test_c_cpp_files)}个):\n"
        for input_file in self.test_c_cpp_files.values():
            string+=f"{input_file}\n"
        return string    

class OutputProject:
    def __init__(self,project_path:str):
        self.project_path=project_path
        self.src_files:Dict[str,OutputFile]={}
        self.tests_files:Dict[str,OutputFile]={}

    def __str__(self):
        string=f"project_path={self.project_path}\n"    
        string+=f"src_files({len(self.src_files)}个):\n"
        for output_file in self.src_files.values():
            string+=f"{output_file}\n"
        string+=f"tests_files({len(self.tests_files)}个):\n" 
        for output_file in self.tests_files.values():
            string+=f"{output_file}\n"
        return string    

class TranslationTask:
    def __init__(self,input_file:InputFile,log_dir_path:str,verify_dir_path:str):
        self.input_file=input_file
        self.log_dir_path=log_dir_path
        self.verify_dir_path=verify_dir_path
        self.all_need_mod_ok=False
        self.prompt=""
        self.request_count=0
        self.translation_parameter_list:List[TranslationParameter] = []
        self.stage1_best:TranslationParameter=None
        self.prompt2=""
        self.best:TranslationParameter=None
        self.translation_result:TranslationResult=None

class TranslationParameter:
    def __init__(self,translation_task:TranslationTask,llm_type:LLMType,llm_model:str):
        self.parent=translation_task
        self.parent.request_count+=1
        self.request_number=self.parent.request_count
        self.verify_name=os.path.basename(self.parent.verify_dir_path)+str(self.request_number)
        self.verify_project_path=os.path.join(self.parent.verify_dir_path,self.verify_name)
        self.log_path=os.path.join(self.parent.log_dir_path,f"{self.verify_name}.log")
        self.llm_type=llm_type
        self.llm_model=llm_model
        self.response_err_msg=""
        self.rust_code=""
        self.forbidden_str_count=0
        self.build_error_count=0
        self.private_problem_count=0
        self.test_failed_count=0
        self.cargo_output=""
        self.translation_result:TranslationResult=None

class StatisticData:
    def __init__(self,model:str):
        self.model=model
        self.request_count=0
        self.response_success_count=0
        self.response_success_rate=0.0
        self.verify_success_count=0
        self.verify_success_rate=0.0

    def __str__(self):
        return f"请求次数：{self.request_count}，响应成功次数：{self.response_success_count}，响应成功率：{self.response_success_rate:.2%}，验证成功次数：{self.verify_success_count}，验证成功率：{self.verify_success_rate:.2%}，模型：{self.model}"

volcengine_client=OpenAI(api_key=VOLCENGINE_API_KEY,base_url=VOLCENGINE_BASE_URL)
qwen_client = OpenAI(api_key=QWEN_API_KEY,base_url=QWEN_BASE_URL)
all_request_count=0
all_response_success_count=0
all_token_count=0
all_context_length=0
translation_task_success_count=0
translation_task_list:List[TranslationTask] = []
statistics_dict:Dict[str, StatisticData] = {}
output_project_name=""
current_python_parent_dir_path=""
input_project:InputProject=None
output_project:OutputProject=None
llm_info_map:Dict[LLMType,LLMInfo] = {
LLMType.VOLCENGINE:LLMInfo(LLMType.VOLCENGINE,VOLCENGINE_MODELS,volcengine_client),
LLMType.QWEN:LLMInfo(LLMType.QWEN,QWEN_MODELS,qwen_client)
}

def init():
    for llm_info in llm_info_map.values():
        for model in llm_info.models:
            statistics_dict[model]=StatisticData(model) 

def statistical_result(log_path):
    for key in statistics_dict:
        if statistics_dict[key].request_count!=0:
            statistics_dict[key].response_success_rate=statistics_dict[key].response_success_count/statistics_dict[key].request_count
            statistics_dict[key].verify_success_rate=statistics_dict[key].verify_success_count/statistics_dict[key].request_count
    sorted_dict = dict(sorted(statistics_dict.items(), key=lambda item: item[1].verify_success_rate, reverse=True))
    log="统计结果：\n"
    for value in sorted_dict.values():
        log+=f"{value}\n"
    log+="\n"
    output_log(log_path,log)

def create_file_dir(file_path):
    file_dir = os.path.dirname(file_path)
    os.makedirs(file_dir, exist_ok=True)

def read_text(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
        return file.read()	

def write_text(file_path, content):
    create_file_dir(file_path)
    with open(file_path, 'w', encoding='utf-8') as file:
        file.write(content)

def write_append(file_path, content):
    create_file_dir(file_path)
    with open(file_path, 'a', encoding='utf-8') as file:
        file.write(content)

def output_log(log_path,log,is_show_in_console=False):
    if is_show_in_console:
        print(log,end="")
    write_append(log_path,log)

def call_cmd(cmd,cwd):
    result = subprocess.run(cmd,text=True,cwd=cwd,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,encoding="utf-8")
    return result.stdout    

def error_message_handle(input_string):
    lines = input_string.split('\n')
    if len(lines) > 0:
        lines.pop(0)
    result = '\n'.join(lines)
    result.replace(current_python_parent_dir_path,"")
    return result

def rename_dir_or_file_for_rust(name):
    name=name.lower()
    new_name=""
    if name[0].isdigit():
        new_name="r"
    for char in name:
        if char.isalpha() or char.isdigit() or char=='_':
            new_name+=char
        else:
            new_name+='_'
    return new_name      

def ai_translation(param:TranslationParameter):
    global all_token_count
    global all_context_length
    global all_request_count
    global all_response_success_count
    global statistics_dict
    
    messages=[]
    prompt=param.parent.prompt
    if param.parent.prompt2!="":
        prompt=param.parent.prompt2
    messages.append({"role": "user", "content": prompt})
    context_length=len(prompt)
    log_path=param.log_path
    verify_name=param.verify_name
    rust_code=""
    err_msg=""
    log=f"向{param.llm_type.value}{param.llm_model}提问{param.request_number}：\n{prompt}\n\n"
    output_log(log_path,log)
    log=f"{verify_name}开始由{param.llm_model}翻译...\n"
    output_log(log_path,log)
    start_time = time.time()
    all_request_count+=1
    statistics_dict[param.llm_model].request_count+=1
    llm_client=llm_info_map[param.llm_type].client
    response = llm_client.chat.completions.create(
        max_tokens=8192,
        model=param.llm_model,
        messages=messages
    )
    end_time = time.time()
    response_time=end_time-start_time
    log=f"{param.llm_type.value}{param.llm_model}响应{param.request_number}：\n{response}\n\n"
    output_log(log_path,log)
    choices_item_count=len(response.choices)
    if choices_item_count==0:
        err_msg="没有得到翻译结果"
    elif choices_item_count>1:
        err_msg="返回了多个翻译结果"
    else:
        choice=response.choices[0]
        if choice.finish_reason=="stop":
            message=choice.message
            reasoning_content=""
            if hasattr(message,"reasoning_content"):
                reasoning_content=message.reasoning_content
                log=f"reasoning_content:\n{reasoning_content}\n\n"
                output_log(log_path,log)
            content=message.content
            log=f"content:\n{content}\n\n"
            output_log(log_path,log)
            context_length+=len(content)+len(reasoning_content)
            rust_codes = re.findall(r'```rust\n(.*?)\n```', content, re.DOTALL)
            rust_codes_item_count=len(rust_codes)
            if rust_codes_item_count==0:
                rust_code=content
            elif rust_codes_item_count>1:
                err_msg="返回了多个rust代码块"
            else:
                rust_code=rust_codes[0]
        else:
            if choice.finish_reason=="tool_calls":
                err_msg="模型命中函数"
            elif choice.finish_reason=="length":
                err_msg="到达 tokens 长度上限"    
            elif choice.finish_reason=="content_filter":
                err_msg="模型推理内容被安全审核接口拦截"
            elif choice.finish_reason=="network_error":
                err_msg="模型推理异常"
            else:
                err_msg="未知错误"
    if err_msg!="":
        log=f"{verify_name}翻译异常({param.llm_model})：\n{err_msg}\n\n"
        output_log(log_path,log)
    else:
        all_response_success_count+=1
        statistics_dict[param.llm_model].response_success_count+=1
    total_tokens=response.usage.total_tokens
    
    log=f"{verify_name}接收到{param.llm_model}的翻译结果。\n"
    log+="耗时："+str(round(response_time))+"秒\n"
    log+="上下文tokens数量:"+str(total_tokens)+"\n"
    log+="上下文长度:"+str(context_length)+"\n\n"
    output_log(log_path,log)
    all_token_count+=total_tokens
    all_context_length+=context_length
    param.response_err_msg=err_msg
    param.rust_code=rust_code

def create_rust_lib_project(dir_path,package_dir_name):
    os.makedirs(dir_path,exist_ok=True)
    cmd=f"cargo new {package_dir_name} --lib --vcs none --name {output_project_name}"
    subprocess.run(cmd,shell=True,capture_output=True,text=True,cwd=dir_path)

def show_progress():
    global translation_task_success_count
    total=len(translation_task_list)
    success_count=0
    for translation_task in translation_task_list:
        if translation_task.translation_result==TranslationResult.SUCCESS:
            success_count+=1
    if success_count>translation_task_success_count:
        translation_task_success_count=success_count
        log=f"完成进度：{success_count}/{total}\n"
        print(log)

def translation_thread(param:TranslationParameter):
    input_file=param.parent.input_file
    output_file=input_file.output_file
    ai_translation(param)
    if param.response_err_msg!="":
        param.translation_result=TranslationResult.RESPONSE_EXCEPTION
        return
    verify_name=param.verify_name
    forbidden_strs=["unsafe","extern \"C\"","#[cfg(test)]","main(","mod"]
    forbidden_str_count=0
    for forbidden_str in forbidden_strs:
        if output_file.file_dir==OutputFileDir.TESTS and forbidden_str=="#[cfg(test)]":
            continue
        forbidden_str_count += len(re.findall(r'\b' + re.escape(forbidden_str) + r'\b', param.rust_code))
    param.forbidden_str_count=forbidden_str_count    
    create_rust_lib_project(param.parent.verify_dir_path,verify_name)
    verify_project_path=os.path.join(param.parent.verify_dir_path,verify_name)
    rs_name=output_file.file_name
    rs_folder_name = output_file.file_dir.value
    verify_rs_path=os.path.join(verify_project_path,rs_folder_name,rs_name)
    write_text(verify_rs_path, param.rust_code)
    lib_content=""
    if output_file.file_dir==OutputFileDir.SRC:
        for mod in output_file.need_src_mods.keys():
            src_rs_name=mod+".rs"
            rs_code=output_project.src_files[src_rs_name].file_content
            write_text(os.path.join(verify_project_path,"src",src_rs_name),rs_code)
            lib_content+=f"pub mod {mod};\n"
        mod,ext=os.path.splitext(rs_name)
        lib_content+=f"pub mod {mod};\n"
    elif output_file.file_dir==OutputFileDir.TESTS:
        for mod in output_file.need_src_mods.keys():
            src_rs_name=mod+".rs"
            rs_code=output_project.src_files[src_rs_name].file_content
            write_text(os.path.join(verify_project_path,"src",src_rs_name),rs_code)
            lib_content+=f"pub mod {mod};\n"
    write_text(os.path.join(verify_project_path,"src","lib.rs"), lib_content)
    verify_rust_project(param)
    log=f"验证输出：\n{param.cargo_output}\n\n"
    log+=f"编译错误数量：\n{param.build_error_count}\n"
    log+=f"测试错误数量：\n{param.test_failed_count}\n"
    log+=f"禁用词数量：\n{param.forbidden_str_count}\n\n"
    output_log(param.log_path,log)
    if param.translation_result==TranslationResult.SUCCESS:
        param.parent.translation_result=TranslationResult.SUCCESS
        statistics_dict[param.llm_model].verify_success_count+=1
        log=f"{verify_name}({param.llm_model})验证通过。\n\n"
        output_log(param.log_path,log)
        show_progress()
    else:
        if param.private_problem_count>0:
            log=f"{verify_name}验证失败，私有权限问题。\n\n"
            output_log(param.log_path,log)
            return
        else:
            log=f"{verify_name}验证失败。\n\n"
            output_log(param.log_path,log)

def translation_thread_wrapper(param:TranslationParameter):
    start_time = time.time()
    try:
        translation_thread(param)
    except Exception as e:
        end_time = time.time()
        response_time=end_time-start_time
        log=f"线程{param.verify_name}异常({param.llm_model})：{e}\n"
        log+=f"耗时：{round(response_time)}秒\n\n"
        output_log(param.log_path,log)

def translation_task_thread(translation_task:TranslationTask):
    for i in range(MAX_LOOP_IN_TRANSLATION_TASK_THREAD):
        api_thread_by_file=SINGLE_FILE_THREAD
        translation_parameter_list:List[TranslationParameter]=[]
        for j in range(api_thread_by_file):
                if j<SINGLE_FILE_THREAD_QWEN:
                    llm_type=LLMType.QWEN
                    llm_model=llm_info_map[llm_type].models[0]  
                elif j<SINGLE_FILE_THREAD_QWEN+SINGLE_FILE_THREAD_DEEPSEEK_V3:
                    llm_type=LLMType.VOLCENGINE
                    llm_model=llm_info_map[llm_type].models[1]  
                elif j<SINGLE_FILE_THREAD_QWEN+SINGLE_FILE_THREAD_DEEPSEEK_V3+SINGLE_FILE_THREAD_DOUBAO:
                    llm_type=LLMType.VOLCENGINE
                    llm_model=llm_info_map[llm_type].models[2]       
                else:
                    llm_type=LLMType.VOLCENGINE
                    llm_model=llm_info_map[llm_type].models[0]       
                param=TranslationParameter(translation_task,llm_type,llm_model)
                translation_parameter_list.append(param)
        translation_task.translation_parameter_list.extend(translation_parameter_list)        
        threads=[]
        for param in translation_parameter_list:
            thread=threading.Thread(target=translation_thread_wrapper,args=(param,))
            threads.append(thread)
            thread.start()
        for thread in threads:
            thread.join(timeout=1900)
        best=None
        for param in translation_parameter_list:
            if param.translation_result==TranslationResult.SUCCESS:
                if best is None:
                    best=param
                elif param.forbidden_str_count<best.forbidden_str_count:
                    best=param    
                elif len(param.rust_code)>len(best.rust_code):
                    best=param
        if best is not None:
            translation_task.best=best
            translation_task.input_file.output_file.file_content=best.rust_code
            write_output_file(translation_task.input_file.output_file)
            break
    

def translation_task_thread_wrapper(translation_task:TranslationTask):
    start_time = time.time()
    try:
        translation_task_thread(translation_task)
    except Exception as e:
        end_time = time.time()
        response_time=end_time-start_time
        log=f"线程{translation_task.input_file.file_name}异常：{e}\n"
        log+=f"耗时：{round(response_time)}秒\n\n"

def get_translation_task(output_file_dir:OutputFileDir,output_name:str):
    for translation_task in translation_task_list:
        output_file=translation_task.input_file.output_file
        if output_file.file_dir==output_file_dir and output_file.file_name==output_name:
            return translation_task

def check_need_mods_are_ok(output_file_dir:OutputFileDir,mods:Dict[str,OutputFile]):
    need_mods_are_ok=True
    for mod in mods.keys():
        rs_name=mod+".rs"
        dependent_task=get_translation_task(output_file_dir,rs_name)
        if dependent_task.translation_result!=TranslationResult.SUCCESS:
            need_mods_are_ok=False
            break
    return need_mods_are_ok    
def thread_pool_work(output_file_dir:OutputFileDir):
    for i in range(MAX_LOOP_IN_THREAD_POOL):
        translation_list=[]    
        for translation_task in translation_task_list:
            if translation_task.translation_result==TranslationResult.SUCCESS:
                continue
            if translation_task.prompt2!="":
                continue
            output_file=translation_task.input_file.output_file
            if output_file.file_dir!=output_file_dir:
                continue
            need_mods_are_ok=check_need_mods_are_ok(OutputFileDir.SRC,output_file.need_src_mods)
            translation_task.all_need_mod_ok=need_mods_are_ok
            if need_mods_are_ok:
                if translation_task.prompt=="":
                    if output_file_dir==OutputFileDir.SRC:
                        set_src_prompt(translation_task)
                    elif output_file_dir==OutputFileDir.TESTS:
                        set_tests_prompt(translation_task)    
                elif translation_task.prompt2=="":
                    if output_file_dir==OutputFileDir.SRC:
                        set_src_prompt2(translation_task) 
                    elif output_file_dir==OutputFileDir.TESTS:
                        set_tests_prompt2(translation_task)
                translation_list.append(translation_task)        
        max_workers=len(translation_list)
        if max_workers<=0:
            return
        with ThreadPoolExecutor(max_workers=max_workers) as executor:
            for translation in translation_list:
                future=executor.submit(translation_task_thread_wrapper, translation)

def verify_rust_project(param:TranslationParameter):
    project_path=param.verify_project_path
    check_result_dict={}
    cmd=["cargo","test","--tests","--no-fail-fast","--message-format","short","--","--nocapture"]
    stdout=call_cmd(cmd,project_path)
    private_problem_count=0
    build_error_count=0
    test_failed_count=0
    if stdout!="":
        lines=stdout.splitlines()
        for line in lines:
            if line.startswith("test result: FAILED"):
                match = re.search(r'(\d+) failed', line)
                if match:
                    test_failed_count += int(match.group(1))
                continue
            if line.startswith("error: test failed"):
                test_failed_count+=1
                continue
            msgs=line.split(":")
            if len(msgs)<5:
                continue
            if "error" in msgs[3]:
                build_error_count+=1
                rs_path=os.path.join(project_path,msgs[0])
                if check_result_dict.get(rs_path) is None:
                    check_result_dict[rs_path]=line
                else:
                    check_result_dict[rs_path]+="\n"+line
                if "is private" in msgs[4]:
                    private_problem_count+=1
    
    verify_result=None
    if build_error_count>0:
        verify_result=TranslationResult.BUILD_ERROR
    elif test_failed_count>0:
        verify_result=TranslationResult.TEST_FAILED
    else:
        verify_result=TranslationResult.SUCCESS
    param.build_error_count=build_error_count   
    param.private_problem_count=private_problem_count 
    param.test_failed_count=test_failed_count
    param.cargo_output=stdout
    param.translation_result=verify_result

def translate_not_done_info():
    count=0
    files:List[str]=[]
    for translation in translation_task_list:
        input_file_name=translation.input_file.file_name
        if translation.best is None:
            count+=1
            files.append(input_file_name)
    return (count,files)

def get_include_file_names(content:str):
    include_pattern = re.compile(r'#\s*include\s*[<"]([^">]+)[">]')
    h_file_names = include_pattern.findall(content)
    return h_file_names

def get_include_file_names_by_dir(content:str,file_dir:InputFileDir):
    h_file_names=get_include_file_names(content)
    src_h_file_names=list(input_project.src_h_files.keys())
    test_h_file_names=list(input_project.test_h_files.keys())  
    include_names_in_src = list(set(h_file_names) & set(src_h_file_names))
    include_names_in_test = list(set(h_file_names) & set(test_h_file_names))
    includes_names_in_src_and_test = list(set(include_names_in_src) & set(include_names_in_test))
    if len(includes_names_in_src_and_test)>0:
        if file_dir==InputFileDir.SRC:
            include_names_in_test=[x for x in include_names_in_test if x not in includes_names_in_src_and_test]  
        elif file_dir==InputFileDir.TEST:
            include_names_in_src=[x for x in include_names_in_src if x not in includes_names_in_src_and_test]
    return (include_names_in_src,include_names_in_test)

def set_src_prompt(translation_task:TranslationTask):
    input_file=translation_task.input_file
    output_file=input_file.output_file
    rs_name=output_file.file_name
    prompt=""
    for h_name,h_file in input_file.includes_in_src.items():
        prompt+=f"{h_name}文件内容如下：\n{h_file.file_content}\n\n"
    prompt+=f"{input_file.file_name}文件内容如下：\n{input_file.file_content}\n\n"
    if re.search(r'\bALLOC_TESTING\b', input_file.file_content):
        prompt+=f"在编译时定义宏ALLOC_TESTING。\n"
    prompt+=f"将C/C++文件{input_file.file_name}翻译成一个Rust文件{rs_name}。\n\n"
    if len(output_file.need_src_mods)>0:
        prompt+=f"以下是{rs_name}中需要引入的Rust文件，都在同一个父模块中，引入时以“use super::”开头。\n\n"
        for mod_file in output_file.need_src_mods.values():
            if mod_file.file_content!="":
                prompt+=f"{mod_file.file_name}文件内容如下：\n{mod_file.file_content}\n\n"
    prompt+="注意：\n"
    prompt+="不要引入第三方crate。\n"
    prompt+="要符合Rust最佳实践，采用纯Rust风格。\n"
    prompt+="翻译后的Rust程序不需要与C/C++有任何交互，不要使用任何与C/C++相关的特性。\n"
    prompt+="不要输出任何注释。\n"   
    prompt+="标识符和函数签名在翻译前后尽量保持一致。\n"
    prompt+="翻译要完整，不要遗漏功能。\n"
    prompt+="在.h头文件中声明和定义的所有内容翻译成Rust时，一定要将可见性全部设为pub，包括结构体中的字段、typedef类型定义等。\n"
    prompt+=f"只输出完整连贯的{rs_name}文件内容，不要输出除此之外的任何内容。\n\n"
    translation_task.prompt=prompt

def set_stage1_best(translation_task:TranslationTask):
    stage1_best:TranslationParameter=None
    for param in translation_task.translation_parameter_list:
        if param.translation_result is not None:
            stage1_best=param
            break
    if stage1_best is None:
        return
    for param in translation_task.translation_parameter_list:
        if param.translation_result is None:
            continue
        if param.translation_result.value>stage1_best.translation_result.value:
            stage1_best=param
        elif param.translation_result.value==stage1_best.translation_result.value:
            if param.build_error_count<stage1_best.build_error_count:
                stage1_best=param
            elif param.build_error_count==stage1_best.build_error_count:    
                if param.test_failed_count<stage1_best.test_failed_count:
                    stage1_best=param
                elif param.test_failed_count==stage1_best.test_failed_count:    
                    if param.forbidden_str_count<stage1_best.forbidden_str_count:
                        stage1_best=param        
    translation_task.stage1_best=stage1_best

def set_src_prompt2(translation_task:TranslationTask):
    set_stage1_best(translation_task)
    stage1_best=translation_task.stage1_best
    if stage1_best is None:
        return
    output_file=translation_task.input_file.output_file
    rs_name=output_file.file_name
    prompt=""
    prompt+=f"{rs_name}文件内容如下：\n{stage1_best.rust_code}\n\n"
    if len(output_file.need_src_mods)>0:
        prompt+=f"以下是{rs_name}中需要引入的Rust文件，都在同一个父模块中，引入时以“use super::”开头。\n\n"
        for src_rs_file in output_file.need_src_mods.values():
                prompt+=f"{src_rs_file.file_name}文件内容如下：\n{src_rs_file.file_content}\n\n"
    prompt+=f"根据以下信息将{rs_name}的代码修改正确：\n{error_message_handle(stage1_best.cargo_output)}\n"
    prompt+=f"只输出修改正确后的完整连贯的{rs_name}文件内容，不要输出除此之外的任何内容。\n\n"
    translation_task.prompt2=prompt

def get_rust_verify_project_prompt(translation_task:TranslationTask):
    input_file=translation_task.input_file
    output_file=input_file.output_file
    prompt="已有一个Rust项目，结构如下：\n"
    prompt+=f"{output_project_name}/\n"
    prompt+="    src/\n" 
    for src_rs_file in output_file.need_src_mods.values():
        prompt+=f"        {src_rs_file.file_name}\n"
    prompt+=f"        lib.rs\n"
    prompt+="    tests/\n"
    prompt+=f"        {output_file.file_name}\n"
    prompt+="    Cargo.toml\n\n"
    for src_rs_file in output_file.need_src_mods.values():
        prompt+=f"src/{src_rs_file.file_name}文件内容如下：\n"
        rs_code=src_rs_file.file_content
        prompt+=f"{rs_code}\n\n"
    prompt+="src/lib.rs文件内容如下：\n"    
    for mod_name in output_file.need_src_mods.keys():
        prompt+=f"pub mod {mod_name};\n"
    prompt+="\n"    
    if translation_task.stage1_best is not None:
        prompt+=f"tests/{output_file.file_name}文件内容如下：\n"
        prompt+=f"{translation_task.stage1_best.rust_code}\n\n"    
    prompt+=f"""Cargo.toml文件内容如下：
[package]
name = "{output_project_name}"
version = "0.1.0"
edition = "2021"

[dependencies]



"""
    return prompt

def set_tests_prompt(translation_task:TranslationTask):
    input_file=translation_task.input_file
    output_file=input_file.output_file
    prompt=""
    for h_file in input_file.includes_in_src.values():
        h_name=h_file.file_name
        prompt+=f"{h_name}文件内容如下：\n{h_file.file_content}\n\n"
    prompt+=f"{input_file.file_name}文件内容如下：\n{input_file.file_content}\n\n"
    prompt+=get_rust_verify_project_prompt(translation_task)
    prompt+=f"将C/C++文件{input_file.file_name}翻译成Rust集成测试文件tests/{output_file.file_name}。\n"
    prompt+="注意：\n"
    prompt+="不要引入第三方crate。\n"
    prompt+="要符合Rust最佳实践，采用纯Rust风格。\n"
    prompt+="翻译后的Rust程序不需要与C/C++有任何交互，不要使用任何与C/C++相关的特性。\n"
    prompt+="不要输出任何注释。\n" 
    prompt+="标识符和函数签名在翻译前后尽量保持一致。\n"
    prompt+="翻译要完整，不要遗漏功能。\n"
    prompt+="一定要计算出num_assert的值并输出。\n"
    prompt+="只在main方法上加上#[test]，其它方法都不加。\n"
    prompt+=f"引入src中的模块时以“use {output_project_name}::”开头。\n"
    prompt+=f"只输出完整连贯的tests/{output_file.file_name}文件内容，不要输出除此之外的任何内容。\n\n"
    translation_task.prompt=prompt

def set_tests_prompt2(translation_task:TranslationTask):
    set_stage1_best(translation_task)
    stage1_best=translation_task.stage1_best
    if stage1_best is None:
        return
    input_file=translation_task.input_file
    output_file=input_file.output_file
    prompt=get_rust_verify_project_prompt(translation_task)
    prompt+=f"根据以下信息将tests/{output_file.file_name}修改正确：\n{error_message_handle(stage1_best.cargo_output)}\n"
    prompt+=f"只输出修改正确后的完整连贯的tests/{output_file.file_name}文件内容，不要输出除此之外的任何内容。\n\n"
    translation_task.prompt2=prompt

def add_translation_file(input_files:Dict[str,InputFile],log_dir_base_path:str,verify_dir_base_path:str):
    global translation_task_list
    for input_file in input_files.values():
        output_file=input_file.output_file
        if output_file is None:
            continue
        input_base_name, input_ext_name = os.path.splitext(input_file.file_name)
        output_base_name,output_ext_name = os.path.splitext(output_file.file_name)
        log_dir_path = os.path.join(log_dir_base_path,input_base_name)
        verify_dir_path=os.path.join(verify_dir_base_path,output_base_name)
        translation_task=TranslationTask(input_file,log_dir_path,verify_dir_path)    
        translation_task_list.append(translation_task) 

def read_input_files(input_dir_path:str):
    input_dir_name=os.path.basename(input_dir_path)
    input_file_dir:InputFileDir=InputFileDir(input_dir_name)
    h_files:Dict[str,InputFile]={}
    c_cpp_files:Dict[str,InputFile]={}
    if input_file_dir==InputFileDir.SRC:
        h_files=input_project.src_h_files
        c_cpp_files=input_project.src_c_cpp_files
    elif input_file_dir==InputFileDir.TEST: 
        h_files=input_project.test_h_files
        c_cpp_files=input_project.test_c_cpp_files
    file_names = os.listdir(input_dir_path)   
    for file_name in file_names:
        input_file=InputFile(input_file_dir,file_name,read_text(os.path.join(input_dir_path,file_name)))
        base_name, ext_name = os.path.splitext(file_name)
        if ext_name==".h":
            if file_name in ["alloc-testing.h","framework.h"]:
                input_file.file_dir=InputFileDir.SRC
                input_project.src_h_files[file_name]=input_file
            else:    
                h_files[file_name]=input_file
        elif ext_name==".c" or ext_name==".cpp":
            if file_name in ["alloc-testing.c","framework.c"]:
                input_file.file_dir=InputFileDir.SRC
                input_project.src_c_cpp_files[file_name]=input_file
            else:    
                c_cpp_files[file_name]=input_file

def analyse_include(input_files:Dict[str,InputFile]):
    for input_file in input_files.values():
        (include_names_in_src,include_names_in_test)=get_include_file_names_by_dir(input_file.file_content,input_file.file_dir)
        for include_name in include_names_in_src:
            input_file.includes_in_src[include_name]=input_project.src_h_files[include_name]
            if input_file.file_dir==InputFileDir.SRC:
                input_project.src_h_files[include_name].be_included_in_src[input_file.file_name]=input_file
            elif input_file.file_dir==InputFileDir.TEST:
                input_project.src_h_files[include_name].be_included_in_test[input_file.file_name]=input_file    

def design_output_file(input_files:Dict[str,InputFile]):
    for input_file in input_files.values():
        (input_basename,input_ext)=os.path.splitext(input_file.file_name)
        if input_ext==".h" and input_file.file_dir==InputFileDir.SRC and len(input_file.be_included_in_src)>0:
            continue
        elif input_ext==".h" and input_file.file_dir==InputFileDir.TEST:
            continue
        output_basename=rename_dir_or_file_for_rust(input_basename)
        output_file_name=output_basename+".rs"
        output_file_dir=None
        if input_file.file_dir==InputFileDir.SRC:
            output_file_dir=OutputFileDir.SRC
        elif input_file.file_dir==InputFileDir.TEST:
            output_file_dir=OutputFileDir.TESTS
        output_file=OutputFile(output_file_dir,output_file_name)
        input_file.output_file=output_file
        if output_file_dir==OutputFileDir.SRC:
            output_project.src_files[output_file_name]=output_file
        elif output_file_dir==OutputFileDir.TESTS:  
            output_project.tests_files[output_file_name]=output_file    
    for input_file in input_files.values():
        if input_file.output_file is None:
            continue
        need_src_mods:Dict[str,OutputFile]={}
        for include_file in input_file.includes_in_src.values():
            (basename,ext)=os.path.splitext(include_file.file_name)
            mod_name=rename_dir_or_file_for_rust(basename)
            mod_file_name=mod_name+".rs"
            if input_file.output_file.file_dir==OutputFileDir.SRC and input_file.output_file.file_name==mod_file_name:
                continue
            need_src_mods[mod_name]=output_project.src_files[mod_file_name]
        input_file.output_file.need_src_mods=need_src_mods    

def translation_project(input_project_path, output_project_path,project_log_dir_path,project_verify_dir_path):
    global input_project
    global output_project

    input_src_path=os.path.join(input_project_path,"src")
    input_test_path=os.path.join(input_project_path,"test")
    log_src_path=os.path.join(project_log_dir_path,"src")
    log_test_path=os.path.join(project_log_dir_path,"test")
    verify_src_path=os.path.join(project_verify_dir_path,"src")
    verify_tests_path=os.path.join(project_verify_dir_path,"tests")
    output_src_path=os.path.join(output_project_path,"src")

    input_project=InputProject(input_project_path)
    output_project=OutputProject(output_project_path)

    read_input_files(input_src_path)
    read_input_files(input_test_path)

    analyse_include(input_project.src_h_files)
    analyse_include(input_project.src_c_cpp_files)
    analyse_include(input_project.test_c_cpp_files)   

    design_output_file(input_project.src_h_files)
    design_output_file(input_project.src_c_cpp_files)
    design_output_file(input_project.test_c_cpp_files)

    add_translation_file(input_project.src_h_files,log_src_path,verify_src_path)
    add_translation_file(input_project.src_c_cpp_files,log_src_path,verify_src_path)
    add_translation_file(input_project.test_c_cpp_files,log_test_path,verify_tests_path)
   
    log=f"{len(translation_task_list)}个C/C++文件需要翻译，开始翻译...\n"
    print(log)
    thread_pool_work(OutputFileDir.SRC)
    thread_pool_work(OutputFileDir.TESTS)

    lib_content=""
    for output_file in output_project.src_files.values():
        if output_file.file_content!="":
            file_basename, file_ext = os.path.splitext(output_file.file_name)
            lib_content+=f"pub mod {file_basename};\n"
    lib_rs_path = os.path.join(output_src_path, "lib.rs")
    write_text(lib_rs_path,lib_content)

def write_output_file(output_file:OutputFile):
    output_file_path=os.path.join(output_project.project_path,output_file.file_dir.value,output_file.file_name)
    write_text(output_file_path,output_file.file_content) 

def rm_dir_if_exist(dir_path):
      if os.path.exists(dir_path):
        shutil.rmtree(dir_path)

def cargo_test_and_output_log(rust_project_path):
    parent_dir=os.path.dirname(rust_project_path)
    project_name= os.path.basename(rust_project_path)
    log_path=os.path.join(parent_dir,project_name+".log")

    log="开始运行cargo build...\n\n"
    output_log(log_path,log)
    cmd=["cargo", "build"]
    stdout =call_cmd(cmd,rust_project_path)
    log="运行结果:\n"
    log+=stdout+"\n\n"
    output_log(log_path,log,False)

    log="开始运行cargo test...\n\n"
    output_log(log_path,log)
    cmd=["cargo","test","--tests","--no-fail-fast","--","--nocapture"]
    stdout = call_cmd(cmd,rust_project_path)
    log="运行结果:\n"
    log+=stdout+"\n\n"
    output_log(log_path,log,False)

def main():
    global output_project_name
    global current_python_parent_dir_path
    start_datetime=datetime.now()
    start_time = time.time()
    init()
    current_file_path = os.path.abspath(__file__)
    current_dir_path = os.path.dirname(current_file_path)
    logs_dir_path = os.path.join(current_dir_path, "logs")
    verify_dir_path=os.path.join(current_dir_path, "verify")
    rm_dir_if_exist(verify_dir_path)
    parent_dir_path = os.path.dirname(current_dir_path)
    current_python_parent_dir_path=parent_dir_path
    input_path = os.path.join(parent_dir_path, INPUT_BASE_DIR)
    output_path = os.path.join(parent_dir_path, OUTPUT_BASE_DIR)
    rm_dir_if_exist(output_path)
    formatted_date = start_datetime.strftime("%Y%m%d%H%M%S")
    input_project_path=os.path.join(input_path, INPUT_PROJECT_DIR)
    output_project_name=rename_dir_or_file_for_rust(INPUT_PROJECT_DIR)
    output_project_path = os.path.join(output_path, output_project_name)
    project_log_dir_path = os.path.join(logs_dir_path,formatted_date,INPUT_PROJECT_DIR)
    project_verify_dir_path=os.path.join(verify_dir_path, INPUT_PROJECT_DIR)
    create_rust_lib_project(output_path,output_project_name)
    translation_project(input_project_path,output_project_path,project_log_dir_path,project_verify_dir_path)
    cargo_test_and_output_log(output_project_path)
    all_translation_file_count=len(translation_task_list)
    (translate_fail_count,translate_fail_files) =translate_not_done_info()
    translation_done_count = all_translation_file_count-translate_fail_count
    end_time = time.time()
    run_time = end_time - start_time
    log_path=os.path.join(current_dir_path,"run.log")   
    log=f"启动时间：{start_datetime.strftime('%Y-%m-%d %H:%M:%S')}\n"
    output_log(log_path,log)
    log="已翻译完毕。\n"
    minute = int(run_time // 60)
    senond = int(run_time % 60)
    log+=f"运行时长：{minute}分 {senond}秒\n"
    log+=f"翻译文件总数：{all_translation_file_count}\n"
    log+=f"翻译成功文件数：{translation_done_count}\n"
    log+=f"翻译失败文件数：{translate_fail_count}\n"
    output_log(log_path,log,True)
    log=f"总请求次数：{all_request_count}\n"
    log+=f"总响应成功次数：{all_response_success_count}\n"
    log+=f"总token数：{all_token_count}\n"
    log+=f"总上下文长度：{all_context_length}\n\n"
    log+=f"翻译失败文件列表：\n"
    for file in translate_fail_files:
        log+=f"{file}\n"
    log+="\n"
    output_log(log_path,log)
    statistical_result(log_path)

main()    